/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * @author Xiao-Feng Li, 2006/10/25
 */
#ifndef _GC_METADATA_H_
#define _GC_METADATA_H_

#include "gc_common.h"
#include "../utils/vector_block.h"
#include "../utils/sync_pool.h"
#include "../thread/collector.h"
#include "../thread/mutator.h"

#define GC_METADATA_SEGMENT_NUM 256

typedef struct GC_Metadata{
  void *segments[GC_METADATA_SEGMENT_NUM]; /* address array of malloced segments for free pool */
  unsigned int num_alloc_segs; /* allocated segment number */
  SpinLock  alloc_lock;
    
  Pool* free_task_pool; /* list of free buffers for mark tasks */
  Pool* mark_task_pool; /* list of mark tasks */
  
  /* FIXME:: the mutator remset pool can be merged with the rootset pool*/
  Pool* free_set_pool; /* list of free buffers for rootsets remsets */
  Pool* gc_rootset_pool; /* list of root sets for enumeration */
  Pool* gc_uncompressed_rootset_pool; /* list of uncompressed root sets for enumeration */
  Pool* mutator_remset_pool; /* list of remsets generated by app during execution */
  Pool* collector_remset_pool; /* list of remsets generated by gc during collection */
  Pool* collector_repset_pool; /* list of repointed ref slot sets */
  Pool* weakroot_pool; /* list of short weak roots */
#ifdef USE_32BITS_HASHCODE
  Pool* collector_hashcode_pool;
#endif

  Pool* gc_dirty_set_pool;

}GC_Metadata;

extern GC_Metadata gc_metadata;

void gc_metadata_initialize(GC* gc);
void gc_metadata_destruct(GC* gc);
void gc_metadata_verify(GC* gc, Boolean is_before_gc);

void gc_set_rootset(GC* gc);
void gc_reset_rootset(GC* gc);
void gc_clear_rootset(GC* gc);
void gc_fix_rootset(Collector* collector, Boolean double_fix);

void gc_clear_remset(GC* gc);

void gc_prepare_dirty_set(GC* gc);
void gc_copy_local_dirty_set_to_global(GC *gc);
void gc_reset_dirty_set(GC* gc);
void gc_clear_dirty_set(GC* gc);

void gc_identify_dead_weak_roots(GC *gc);
void gc_update_weak_roots(GC *gc, Boolean double_fix);
void gc_reset_collectors_rem_set(GC *gc); 

inline void  gc_task_pool_clear(Pool* task_pool)
{
  Vector_Block* task = pool_get_entry(task_pool);
  while(task){
    vector_stack_clear(task);  
    pool_put_entry(gc_metadata.free_task_pool, task);
    task = pool_get_entry(task_pool);
  } 
  return;
}

inline void  gc_set_pool_clear(Pool* set_pool)
{
  Vector_Block* set = pool_get_entry(set_pool);
  while(set){
    vector_block_clear(set);
    pool_put_entry(gc_metadata.free_set_pool, set);
    set = pool_get_entry(set_pool);
  } 
  return;  
}

Vector_Block* gc_metadata_extend(Pool* pool);

inline Vector_Block *free_set_pool_get_entry(GC_Metadata *metadata)
{
  Vector_Block *block = pool_get_entry(metadata->free_set_pool);

  while(!block)
      block = gc_metadata_extend(metadata->free_set_pool);
  
  assert(vector_block_is_empty(block));
  return block;
}

void free_set_pool_put_entry(Vector_Block* block, GC_Metadata *metadata);

inline Vector_Block *free_task_pool_get_entry(GC_Metadata *metadata)
{
  Vector_Block *block = pool_get_entry(metadata->free_task_pool);

  while(!block)
      block = gc_metadata_extend(metadata->free_task_pool);
   
  assert(vector_block_is_empty(block));
  return block;
}

inline void mutator_remset_add_entry(Mutator* mutator, REF* p_ref)
{
  assert( p_ref >= gc_heap_base_address() && p_ref < gc_heap_ceiling_address()); 
  
  Vector_Block* root_set = mutator->rem_set;  
  vector_block_add_entry(root_set, (POINTER_SIZE_INT)p_ref);
  
  if( !vector_block_is_full(root_set)) return;
    
  pool_put_entry(gc_metadata.mutator_remset_pool, root_set);
  mutator->rem_set = free_set_pool_get_entry(&gc_metadata);  
  assert(mutator->rem_set);
}

inline void mutator_dirtyset_add_entry(Mutator* mutator, Partial_Reveal_Object* p_obj)
{
  Vector_Block* dirty_set = mutator->dirty_set;    
  mutator->dirty_obj_slot_num++;
  vector_block_add_entry(dirty_set, (POINTER_SIZE_INT)p_obj);

  if( !vector_block_is_full(dirty_set) ) {
       return;
  }

  /*
  the vector is mutator local here, but it is not thread safe in concurren gc 
  we use it here with lock to prevent it has been already put into global dirty pool when conclctors are checking the convergence condition
  if yes, it may has been read out by conclctors, the header pointer++, 
  only when they are meet at some position (it is empty AND full now), we need clear it to reuse.
  otherwise, replace it with a new one
  */
  lock(mutator->dirty_set_lock);  
  if( vector_block_is_empty(dirty_set) ) {
  	vector_block_clear(dirty_set);
	unlock(mutator->dirty_set_lock);
       return;
  }
  pool_put_entry(gc_metadata.gc_dirty_set_pool, dirty_set);
  mutator->dirty_set = free_set_pool_get_entry(&gc_metadata);
  unlock(mutator->dirty_set_lock);
}

inline void collector_repset_add_entry(Collector* collector, Partial_Reveal_Object** p_ref)
{
//  assert( p_ref >= gc_heap_base_address() && p_ref < gc_heap_ceiling_address()); 

  Vector_Block* root_set = collector->rep_set;  
  vector_block_add_entry(root_set, (POINTER_SIZE_INT)p_ref);
  
  if( !vector_block_is_full(root_set)) return;
    
  pool_put_entry(gc_metadata.collector_repset_pool, root_set);
  collector->rep_set = free_set_pool_get_entry(&gc_metadata);  
  assert(collector->rep_set);
}

inline void collector_remset_add_entry(Collector* collector, Partial_Reveal_Object** p_ref)
{
  //assert( p_ref >= gc_heap_base_address() && p_ref < gc_heap_ceiling_address()); 

  Vector_Block* root_set = collector->rem_set;  
  vector_block_add_entry(root_set, (POINTER_SIZE_INT)p_ref);
  
  if( !vector_block_is_full(root_set)) return;
    
  pool_put_entry(gc_metadata.collector_remset_pool, root_set);
  collector->rem_set = free_set_pool_get_entry(&gc_metadata);  
  assert(collector->rem_set);
}

#ifdef USE_32BITS_HASHCODE  
inline void collector_hashcodeset_add_entry(Collector* collector, Partial_Reveal_Object** p_ref)
{
  Vector_Block* hashcode_set = collector->hashcode_set;
  vector_block_add_entry(hashcode_set, (POINTER_SIZE_INT) p_ref);

  if(!vector_block_is_full(hashcode_set)) return;

  pool_put_entry(gc_metadata.collector_hashcode_pool, hashcode_set);
  collector->hashcode_set = free_set_pool_get_entry(&gc_metadata);
  assert(collector->hashcode_set);
}
#endif

FORCE_INLINE void collector_tracestack_push(Collector* collector, void* p_task)
{
  /* we don't have assert as others because p_task is a p_obj for marking,
     or a p_ref for trace forwarding. The latter can be a root set pointer */
  Vector_Block* trace_task = (Vector_Block*)collector->trace_stack;  
  vector_stack_push(trace_task, (POINTER_SIZE_INT)p_task);
  
  if( !vector_stack_is_full(trace_task)) return;
    
  pool_put_entry(gc_metadata.mark_task_pool, trace_task);
  collector->trace_stack = free_task_pool_get_entry(&gc_metadata);  
  assert(collector->trace_stack);
}

inline void gc_weak_rootset_add_entry(GC* gc, Partial_Reveal_Object** p_ref, Boolean is_short_weak)
{
  //assert(is_short_weak == FALSE); //Currently no need for short_weak_roots
  assert( p_ref < gc_heap_base_address() || p_ref >= gc_heap_ceiling_address()); 
  
  Vector_Block* weakroot_set = gc->weakroot_set;
  vector_block_add_entry(weakroot_set, (POINTER_SIZE_INT)p_ref);
  
  if( !vector_block_is_full(weakroot_set)) return;

  pool_put_entry(gc_metadata.weakroot_pool, weakroot_set);
  gc->weakroot_set = free_set_pool_get_entry(&gc_metadata);  
  assert(gc->weakroot_set);
}

extern unsigned int rootset_type;

enum ROOTSET_TYPE{
  ROOTSET_IS_OBJ = 0x01,
  ROOTSET_IS_REF = 0x02
};


inline void gc_set_rootset_type(unsigned int rs_type)
{
  rootset_type = rs_type;
}

#ifdef COMPRESS_REFERENCE

inline void gc_rootset_add_entry(GC* gc, Partial_Reveal_Object** p_ref)
{
  assert( p_ref < gc_heap_base(gc) || p_ref >= gc_heap_ceiling(gc));
  
  GC_Metadata *metadata = gc->metadata;
  Vector_Block *uncompressed_root_set = gc->uncompressed_root_set;
  assert(uncompressed_root_set);
  
  Partial_Reveal_Object *p_obj = *p_ref;
  REF ref = obj_ptr_to_ref(p_obj);
  
  /* construct an Uncompressed_Root */
  vector_block_add_entry(uncompressed_root_set, (POINTER_SIZE_INT)p_ref);
  assert(!vector_block_is_full(uncompressed_root_set));
  if(rootset_type == ROOTSET_IS_REF)
    vector_block_add_entry(uncompressed_root_set, (POINTER_SIZE_INT)ref);
  else if(rootset_type == ROOTSET_IS_OBJ)
    vector_block_add_entry(uncompressed_root_set, (POINTER_SIZE_INT)p_obj);
  
  if(!vector_block_is_full(uncompressed_root_set)) return;
  
  pool_put_entry(metadata->gc_uncompressed_rootset_pool, uncompressed_root_set);
  gc->uncompressed_root_set = free_set_pool_get_entry(metadata);
  assert(gc->uncompressed_root_set);
}

#else /* COMPRESS_REFERENCE */
inline void gc_rootset_add_entry(GC* gc, Partial_Reveal_Object** p_ref)
{
  assert( p_ref < gc_heap_base_address() || p_ref >= gc_heap_ceiling_address()); 

  Partial_Reveal_Object *p_obj = *p_ref;
  
  Vector_Block* root_set = gc->root_set;

  if(rootset_type == ROOTSET_IS_REF)
    vector_block_add_entry(root_set, (POINTER_SIZE_INT)p_ref);
  else if(rootset_type == ROOTSET_IS_OBJ)
    vector_block_add_entry(root_set, (POINTER_SIZE_INT)p_obj);
  
  if( !vector_block_is_full(root_set)) return;
    
  pool_put_entry(gc_metadata.gc_rootset_pool, root_set);
  gc->root_set = free_set_pool_get_entry(&gc_metadata);  
  assert(gc->root_set);
}
#endif

#endif /* #ifndef _GC_METADATA_H_ */
